Plotlyの基本#

Plotlyは、JavaScriptを基盤としたインタラクティブなデータ可視化を実現するためのライブラリで、グラフやチャートを簡単に作成することができます。 幅広いプログラミング言語に対応したインターフェースを提供しており、これにより開発者は自分の得意な言語でデータビジュアライゼーションを行うことができます。 本書では、Pythonを使用してデータ可視化を行うため、Python向けのインターフェースであるplotly.pyを利用します。

Plotlyは非常に奥が深いライブラリですので、その全てを本章で解説することはできません。 本章では、本書で紹介するデータ可視化手法の理解に最低限必要な知識の紹介にとどめるため、詳細は別途公式サイトや専門書をご参照ください。

なお、本書で想定するplotly.pyのバージョンは以下です。

Hide code cell content
import plotly

plotly.__version__
'5.19.0'

なお、本章ではPlotlyの機能を解説することを目的としているため、不完全で見づらいグラフが表示されることがあります。

Plotlyの主要なオブジェクト#

Plotlyで用いる主要なオブジェクトとして、figuretrace、そしてlayoutがあります[@doriller et al., 2020]

  • figure: グラフの描画領域に相当するオブジェクト

  • trace: グラフの実体に相当するオブジェクト

  • layout: グラフの配置やスタイルなどの情報を持ったオブジェクト

本書では後述する高レベルなインターフェース(plotly.express)を用いるため、実は上記を意識せずとも作業を進めることは可能です。 しかし、高度な可視化を行う際や、予期せぬエラーが発生した際、このような前提知識を持っているかどうかで対応の幅が大きく異なります。

説明に入る前に、ライブラリのインポートを済ませておきます。

Hide code cell content
# Pandasをpdとしてインポート
import pandas as pd

# Plotlyの低レベルインターフェースであるgraph_objectsをgoとしてインポート
import plotly.graph_objects as go

本章ではfigureをJupyter上に表示するためのshow_figという関数を用います。 これも事前に定義しておきます。

Hide code cell source
# tag:hide
RENDERER = "plotly_mimetype+notebook"


def show_fig(fig):
    """
    所定のレンダラーを用いてplotlyの図を表示
    Jupyter Bookなどの環境での正確な表示を目的とする

    Parameters
    ----------
    fig : Figure
        表示対象のplotly図

    Returns
    -------
    None
    """

    # 図の周囲の余白を設定
    # t: 上余白
    # l: 左余白
    # r: 右余白
    # b: 下余白
    fig.update_layout(margin=dict(t=25, l=25, r=25, b=25))

    # 所定のレンダラーで図を表示
    fig.show(renderer=RENDERER)

figure:グラフの描画領域#

figureは、可視化手法の描画領域全体を表すオブジェクトです。 go.Figureクラスのオブジェクトがこれに対応します。

Hide code cell content
# figとしてFigureクラスのインスタンスを初期化
fig = go.Figure()

では、この時点でfigshow_figするとどうなるでしょうか?

Hide code cell source
# 空のfigureを表示
show_fig(fig)

まだ空っぽです。 Plotlyでは、figuretraceを追加することでグラフを作り上げていきます。

trace:グラフの実体#

traceとはグラフの本体となるオブジェクトです。go.Scatter等がこれに該当します。 マンガデータの具体例を用いて説明します。

Hide code cell content
# マンガ各話データを格納したcm_ce.csvをdf_ceとして読み込み
df_ce = pd.read_csv("../../data/cm/input/cm_ce.csv")

ここでは便宜上、年別の各マンガ雑誌の発行巻号数の推移を可視化することを考えましょう。

Hide code cell content
# df_ceに発行年に関する列(year)を追加
df_ce["year"] = pd.to_datetime(df_ce["date"]).dt.year

# mcname(マンガ雑誌名)、yaer(発行年)別にユニークなmiid(雑誌巻号)数を集計
df_plot = df_ce.groupby(["mcname", "year"])["miid"].nunique().reset_index()
Hide code cell content
# 可視化対象のDataFrameの冒頭5行を表示
df_plot.head()
mcname year miid
0 週刊少年サンデー 1970 21
1 週刊少年サンデー 1971 51
2 週刊少年サンデー 1972 51
3 週刊少年サンデー 1973 51
4 週刊少年サンデー 1974 50

figuretraceを追加する方法は主に二通りです。

  • Figureクラスの初期化時に引数dataとして渡す

  • Figureインスタンスに対してadd_traceメソッドで引数として渡す

まず、前者について説明するために、週刊少年サンデーの年間巻号数の推移を折れ線グラフとして追加します。

Hide code cell source
# mcnameが週刊少年サンデーと一致するデータのみ抽出し、インデックスを張り直す
df_sunday = df_plot[df_plot["mcname"] == "週刊少年サンデー"].reset_index(drop=True)

# go.Scatterで散布図オブジェクトを作成
# x軸として週刊少年サンデーの発売年、y軸として週刊少年サンデーの年間巻号数
trace = go.Scatter(x=df_sunday["year"], y=df_sunday["miid"], name="週刊少年サンデー")

# figとしてFigureクラスのインスタンスを初期化
# このとき、先に定義したtraceをdataとして渡す
fig = go.Figure(data=trace)

# figを表示
show_fig(fig)

次に、add_traceを用いた方法を試します。週刊少年サンデー以外のマンガ雑誌の年間巻号数を追加してみましょう。

Hide code cell source
# df_plotデータフレームを"mcname"カラムでグループ化し、各グループに対して処理を実行
for mcname, df_mc in df_plot.groupby("mcname"):
    # もしグループの名前が"週刊少年サンデー"の場合は、既に追加済みなのでスキップ
    if mcname == "週刊少年サンデー":
        continue

    # グループ化されたデータに対して散布図のトレースを作成
    # x軸には"year"カラムのデータ、y軸には"miid"カラムのデータを使用
    # トレースの名前はmcname(マンガ雑誌名)に設定
    trace_mc = go.Scatter(x=df_mc["year"], y=df_mc["miid"], name=mcname)

    # 作成したトレースを図figに追加
    fig.add_trace(trace_mc)

# 最終的に作成した図を表示
show_fig(fig)

このように、マンガ雑誌ごとにtracefigureに追加することができました。 ちなみに、figureに追加済みのtraceの設定を変更する際は、update_tracesメソッドを利用できます。

Hide code cell source
# update_tracesメソッドを用いて、マーカーを追加し、そのサイズを10に指定
fig.update_traces(mode="lines+markers", marker=dict(size=10))

# 再度figを表示
show_fig(fig)

しかし、このままではまだ各軸のタイトルがありませんし、グラフとしては不完全です。 layoutの調整が必要です。

layout: グラフの配置やスタイル#

layoutは、グラフの配置やスタイル等の情報を格納したオブジェクトです。 go.Layoutクラスのオブジェクトがこれに該当します。

figurelayoutを追加・調整する方法は主に二通りあります:

  • Figureクラスの初期化時に引数layoutとして渡す

  • Figureインスタンスに対してupdate_layoutで直接編集する

本書では前者を用いることはほとんどないため、後者について具体例を用いて解説します。

マンガ雑誌別の年間巻号数の推移を表したfigに対して、update_layoutを用いてX軸ラベルとY軸ラベルを追加してみましょう。

Hide code cell source
# figのlayoutを更新して、x軸とy軸にタイトルを設定
fig.update_layout(xaxis={"title": "発売年"}, yaxis={"title": "年間巻号数"})

# layout更新済みのfigを再表示
show_fig(fig)

figureにX軸ラベルとY軸ラベルを追加することができました。 ここで取り上げたupdate_traceupdate_layoutの用途はほんの一部でしかありません。 非常に柔軟に調整が可能ですので、興味のある方は公式ドキュメントを参照ください。

plotly.expressによる可視化#

plotly.expressは、本書で主に採用するplotly.pyの高レベルなインターフェースです。 本節ではplotly.expressを使ってさまざまなグラフタイプを作成し、カスタマイズする方法を紹介します。

まずはplotly.expressをインポートしましょう。

Hide code cell content
# plolty.expressをpxとしてインポート
import plotly.express as px

グラフの作成#

それでは、先ほどと同様の図をplotly.expressで作成してみましょう。

Hide code cell source
# px.lineを使用して、df_plotデータフレームから折れ線グラフを作成
# x軸には"year"カラムのデータを、y軸には"miid"カラムのデータを使用
# "mcname"カラムの値に基づいて、異なる線の色分けを行う
fig = px.line(df_plot, x="year", y="miid", color="mcname")

# 作成した線グラフを表示
show_fig(fig)

plotly.graph_objectsを使った場合よりはるかに簡単に作成できました。 plotly.expressでは様々な可視化手法に対して統一的なインターフェースを提供しています:

  • 可視化対象のpd.DataFrame

  • X軸として採用するカラム名を指定するx

  • Y軸として採用するカラム名を指定するy

  • 色分けの基準として採用するカラム名を指定するcolor

等はほとんどの可視化手法で共通しています。

グラフの調整#

なお、update_traceupdate_layoutはこれまで同様に利用できるため、例えば次のような調整が可能です。

Hide code cell source
# update_tracesメソッドを用いて、マーカーを追加し、そのサイズを10に指定
fig.update_traces(mode="lines+markers", marker=dict(size=10))

# figのlayoutを更新して、x軸とy軸にタイトルを変更
fig.update_layout(xaxis={"title": "発売年"}, yaxis={"title": "年間巻号数"})

# 更新済みのfigを再表示
show_fig(fig)

高度なグラフの作成#

plotly.expressでは、facetという概念を用いて複数のグラフを同時に表示できます。

  • facet_col: 列方向に複数のグラフを表示する際に、基準となるカラム名を指定

  • facet_row: 行方向に複数のグラフを表示する際に、基準となるカラム名を指定

Hide code cell source
# px.lineを使用して、df_plotデータフレームから折れ線グラフを作成
# x軸には"year"カラムのデータを、y軸には"miid"カラムのデータを使用
# "mcname"カラムの値に基づいてfacetを2列ごと表示する
fig = px.line(df_plot, x="year", y="miid", facet_col="mcname", facet_col_wrap=2)

# 作成した線グラフを表示
show_fig(fig)

また、plotly.expressでは簡単にアニメーションも作成できます。 紙幅の都合で割愛しますが、その一例は8章のモーションチャートに掲載してあります。